
package taskmanager;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import mpiserver.MpiEvent;
import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.PriorityBlockingQueue;
import mpiserver.IMpiServer;
import repository.LongComparable;
import repository.exception.NoRepositoryException;
import repository.exception.NullRootException;

/**
 * Планировщик нижнего уровня
 * Доступен только в рамках пакета "taskmanager"
 * @author Aleksey ID=2001
 */
class LocalTaskManager implements ILocalTaskManager {
    /** ваксимальное число активных задач */
    private int maxActiveCount=0;
    /** вектор выполняемых(активных) задач */
    private Vector<JTask> activeTasksVector;
    /** очередь с приоритетами для невыполняемых задач */
    private PriorityBlockingQueue <JTask> waitingTaskQueue;
    /** таблица соответствия id задачи -> JTask*/
    private ConcurrentHashMap<LongComparable,JTask> taskTable;
    /** сервер mpi */
    private IMpiServer mpiServer;

    /**
     * конструктор
     * @param mpiServer модуль управления сервером mpi
     */
    LocalTaskManager(IMpiServer mpiServer){
        this.mpiServer=mpiServer;
        activeTasksVector = new Vector<JTask>();
        waitingTaskQueue = new PriorityBlockingQueue<JTask>();
        taskTable= new ConcurrentHashMap<LongComparable,JTask>();
    }

    /**
     * Запросить количество задач в планировщике
     * @return число задач в планировщике
     */
    public int getTaskCount(){
        return taskTable.size();
    }

    /**
     * Проверить наличие задач в планировщике
     * @return true - здач нет, иначе false
     */
    public boolean isEmpty(){
        return taskTable.isEmpty();
    }

    /**
     * Установить максимальное количество активных задач
     * @param max максимально количество активных задач
     */
    public void setMaxActiveCount(int max){ maxActiveCount=max;}

    /**
     * Запросить максимальное количество активных задач
     * @return максимально количество активных задач
     */
    public int getMaxActiveCount(){return maxActiveCount;}

    /**
     * Добавить задачу в планировщик
     * @param task задача, не null !
     * @return true - задача добавлена, false - задача с указанным id уже есть
     */
    public boolean addTask(JTask task){
        if (task==null) throw new NullPointerException();
        //проверить наличие задачи в системе
        LongComparable tempID=new LongComparable(task.getTaskID());
        if(taskTable.containsKey(tempID)) return false;
        //установить задачу ожидающей
        task.setStatus(JTask.WAITED);
        //добавить задачу в таблицу и очередь
        taskTable.put(tempID, task);
        waitingTaskQueue.add(task);
        return true;
    }

    /**
     * Удалить задачу из планировщика
     * Процессы связанные с задачей уничтожаются
     * @param taskID идентификатор задачи
     * @return true - задача удалена, false - задачи нет
     */
    public boolean removeTask(long taskID){
        //проверить наличие задачи в системе
        LongComparable tempID=new LongComparable(taskID);
        JTask task=taskTable.get(tempID);
        if(task==null) return false;
        //остановить задачу если она выполняется или выполнялась
        if(task.getStatus()==JTask.ACTIVE || task.getStatus()==JTask.STOPED ){
            //
            //  ВЫЗОВ СЕРВЕРА MPI ДЛЯ УДАЛЕНИЯ ПРОЦЕССА !!!!
            //
            task.setStatus(JTask.STOPED);
        }
        //удалить задачу из таблицы и очередей
        taskTable.remove(tempID);
        waitingTaskQueue.remove(task);
        activeTasksVector.remove(task);
        return true;
    }

    /**
     * Запросить задачу
     * @param taskID идентификатор задачи
     * @return задача или null в случае отсутствия
     */
    public JTask getTask(long taskID){
        return taskTable.get(new LongComparable(taskID));
    }
    
    /**
     * Запросить идентификаторы активных задач
     * @return массив идентификаторов задач
     */
    public long[] getActiveTasks(){
        int sz=activeTasksVector.size();
        long res[]=new long [sz];
        //перебор активных задач и заполнения массива идентификаторов задач
        for(int i=0;i<sz;i++) 
            res[i]=activeTasksVector.get(i).getTaskID();
        return res;
    }
    
    /**
     * Запросить идентификаторы ожидающих и приостановленных задач
     * @return массив идентификаторов задач
     */
    public long[] getWaitTasks(){
        int i=0;
        long res[]=new long [waitingTaskQueue.size()];
        Iterator<JTask> iter=waitingTaskQueue.iterator();
        //перебор ожидающих задач и заполнения массива идентификаторов задач
        while(iter.hasNext()){
            res[i]=iter.next().getTaskID();
            i++;
        }
        return res;
    }

    /**
     * Запросить идентификаторы задач в планировщике
     * @return массив идентификаторов задач
     */
    public long[] getAllTasks(){
        int i=0;
        long res[]=new long [taskTable.size()];
        Iterator<LongComparable> iter=taskTable.keySet().iterator();
        //перебор таблицы задач и заполнения массива идентификаторов задач
        while(iter.hasNext()){
            res[i]=iter.next().value;
            i++;
        }
        return res;
    }

    /**
     * Проверить наличие задачи
     * @param taskID идентификатор задачи
     * @return true - задача присутствует, false иначе
     */
    public boolean containsTask(long taskID){
        return taskTable.containsKey(new LongComparable(taskID));
    }

    /**
     * Остановить выполнение задачи
     * @param taskID идентификатор задачи
     * @return true - задача остановлена, false - задачи нет
     */
    public boolean stopTask(long taskID){
        //проверить наличие задачи в системе
        LongComparable tempID=new LongComparable(taskID);
        JTask task=taskTable.get(tempID);
        if(task==null) return false;
        //остановить задачу
        if(task.getStatus()!=JTask.ACTIVE) return true;
        //
        //  ВЫЗОВ СЕРВЕРА MPI ДЛЯ ОСТАНОВКИ ПРОЦЕССА !!!!
        //
        task.setStatus(JTask.STOPED);
        //удалить задачу из активных
        activeTasksVector.remove(task);
        //добавить в очередь
        waitingTaskQueue.put(task);
        return true;
    }

    /**
     * Отправить следующую задачу на выполнение
     * @return true - успешное выполнение, false - превышен порог активных задач
     * или очередная задача заблокирована
     */
    public boolean activateNextTask(){
        //проверить превышение количества активных задач
        if(activeTasksVector.size()>=maxActiveCount) return false;
        //проверить очередь на пустоту
        if(waitingTaskQueue.isEmpty()) return true;
        //проверить очередную задачу
        JTask task =waitingTaskQueue.peek();
        if(!task.mayActivate()) return false;
        //получить очередную задачу
        task = waitingTaskQueue.poll();
        activeTasksVector.add(task); // << Заглушка. Убрать
        if(task.isCompiled()) //задача откомпилирована
            mpiServer.runMpi(task/*, 4*/);
        else // запустить компиляцию
            mpiServer.runGcc(task);
        return true;
    }

    /**
     * Запросить количество активных задач в планировщике
     * @return количество активных задач
     */
    public int getActiveCount(){
        return activeTasksVector.size();
    }

    /**
     * Проверить наличие свободных позиций для активных задач
     * @return true - заняты все позиции, false - можно активировать задачу
     */
    public boolean isActiveFull(){
        if(activeTasksVector.size()<maxActiveCount) return true;
        return false;
    }

    /**
     * Событие завершения задачи
     * удалить задачу из системы и попытаться активировать следующую
     * @param e событие
     */
    void endingTask(MpiEvent e) {
        //проверить наличие задачи в системе
        LongComparable tempID=new LongComparable(e.getTaskID());
        JTask task=taskTable.get(tempID);
        if(task==null) return;
        //удалить задачу из таблицы и очередей
        taskTable.remove(tempID);
        waitingTaskQueue.remove(task);
        activeTasksVector.remove(task);
        //активировать следующую задачу
        activateNextTask();
    }

    /**
     * Ошибка запуска задачи
     * перенести задачу в список ожидающих и попытаться активировать следующую
     * @param e объект события
     */
    void errorTask(MpiEvent e) {
        LongComparable tempID=new LongComparable(e.getTaskID());
        JTask task=taskTable.get(tempID);
        if(task==null) return;
        //удалить задачу из таблицы и очередей
        waitingTaskQueue.add(task);
        activeTasksVector.remove(task);
        //активировать следующую задачу
        activateNextTask();
    }
}
